<template>
  <view class="mxp-form" :class="[cusClass]" :style="[{ padding: fromPadding, background }, cusStyle]">
    <mxp-line :color="fromBorderColor"></mxp-line>
    <slot></slot>
  </view>
</template>

<script lang="ts" setup>
  import MxpLine from '@/mxp-ui/mxp-line/mxp-line.vue';
  import { publicStyeProps } from '../mxp-tool/utils/public-props';
  import { type CSSProperties, type PropType, computed, nextTick, onBeforeMount, onUnmounted, provide, reactive, watch } from 'vue';
  import type { $AllColorStr, $SizeUnitArr4Str, $SizeUnitStr } from '@/mxp-ui/mxp-tool/types/interface';
  import Schema, { type RuleItem } from '../mxp-tool/sdk/async-validator';
  import { getOnlyID } from '../mxp-tool/utils/common';
  import { type FormID, type MxpFormConfig, type MxpFormProps, formInjectKey, mxpFormStore } from '../mxp-tool/store/mxp-store-form';

  export type $RuleItem = RuleItem & { trigger?: ('blur' | 'change')[] | 'blur' | 'change' };
  export type $Rules<T extends Record<string, unknown> = Record<string, unknown>> = Record<keyof T, $RuleItem | $RuleItem[]>;

  defineOptions({
    name: 'mxp-form',
  });
  const props = defineProps({
    /** 背景色 */
    background: { type: String as PropType<$AllColorStr>, default: undefined },
    /** 绑定的值 */
    model: { type: Object as PropType<Record<number | string, unknown>>, default: () => ({}) },
    /** 验证规则 */
    rules: { type: Object as PropType<$Rules>, default: () => ({}) },
    /** from顶部边框颜色(如不需要边框可设为fasle) */
    fromBorderColor: { type: [Boolean, String] as PropType<$AllColorStr | boolean>, default: true },
    /** 四周边距 */
    fromPadding: { type: String as PropType<$SizeUnitArr4Str>, default: '0px 0px' },
    /** 错误提示方式  message-提示信息; toast-进行toast提示; border-下边框呈现红色; label-显示在lable的右边(仅当labal显示在上方时有效); none-无提示 */
    itemErrorMode: { type: String as PropType<'border' | 'label' | 'message' | 'none'>, default: undefined },
    /** 错误提示文本的对齐方式 */
    itemErrorAlign: { type: String as PropType<'center' | 'left' | 'right'>, default: undefined },
    /** 内边距 */
    itemPadding: { type: String as PropType<$SizeUnitArr4Str>, default: undefined },
    /** 底部边框颜色 */
    itemBorderColor: { type: [Boolean, String] as PropType<$AllColorStr | boolean>, default: undefined },
    /** label的位置，left-左边，top-上边 */
    itemLabelPosition: { type: String as PropType<'left' | 'top'>, default: undefined },
    /** label的宽度 */
    itemLabelWidth: { type: String as PropType<$SizeUnitStr>, default: undefined },
    /** label对齐方式 */
    itemLabelAlign: { type: String as PropType<'center' | 'left' | 'right'>, default: undefined },
    /** content对齐方式 默认为: stretch 如果不为 stretch 里面内容无法铺满整个容器宽度 */
    itemContentAlign: { type: String as PropType<'center' | 'left' | 'right' | 'stretch'>, default: undefined },
    /** 最小高度 (注意最小高度不包含上下 6px margin的高度，且最小高度和margin都是在是在subjectView中设置的) */
    itemMinHeight: { type: String as PropType<$SizeUnitStr>, default: undefined },
    /** 自定义lable的样式 */
    itemLabelTextStyle: { type: Object as PropType<CSSProperties>, default: () => ({}) },
    /** 自定义lable的样式 */
    itemLabelViewStyle: { type: Object as PropType<CSSProperties>, default: () => ({}) },
    /** 主体的样式 */
    itemSubjectViewStyle: { type: Object as PropType<CSSProperties>, default: () => ({}) },
    /** 主体的样式 */
    itemContentViewStyle: { type: Object as PropType<CSSProperties>, default: () => ({}) },
    /** 主体的样式 */
    itemErrorTextStyle: { type: Object as PropType<CSSProperties>, default: () => ({}) },
    /** 底部边框样式 */
    itemLineStyle: { type: Object as PropType<CSSProperties>, default: () => ({}) },
    ...publicStyeProps,
  });
  defineSlots<{
    /** 主体内容 */
    default?: () => void;
  }>();
  const formStore = mxpFormStore();
  const id: FormID = `__MXP-FRPM-${getOnlyID('mxp-form')}__`;
  const formProps = computed<MxpFormProps>(() => {
    const _formProps: MxpFormProps = {
      itemErrorMode: props.itemErrorMode,
      itemErrorAlign: props.itemErrorAlign,
      itemPadding: props.itemPadding,
      itemBorderColor: props.itemBorderColor,
      itemLabelPosition: props.itemLabelPosition,
      itemLabelWidth: props.itemLabelWidth,
      itemLabelAlign: props.itemLabelAlign,
      itemContentAlign: props.itemContentAlign,
      itemMinHeight: props.itemMinHeight,
      itemLabelTextStyle: props.itemLabelTextStyle,
      itemLabelViewStyle: props.itemLabelViewStyle,
      itemSubjectViewStyle: props.itemSubjectViewStyle,
      itemContentViewStyle: props.itemContentViewStyle,
      itemErrorTextStyle: props.itemErrorTextStyle,
      itemLineStyle: props.itemLineStyle,
    };

    return _formProps;
  });
  watch(
    () => formProps.value,
    (val) => {
      formStore.actions.setFormProps(id, val);
    }
  );

  formStore.actions.setFormProps(id, formProps.value);
  formStore.actions.setValidateField(id, validateField);
  provide<FormID>(formInjectKey, id);

  onBeforeMount(() => {
    provide<FormID>(formInjectKey, id);
  });

  onUnmounted(() => {
    // 删除
    formStore.actions.delItemByID(id);
  });

  const data = reactive({
    formRules: null as $Rules | null,
    // 原始的model快照，用于reset方法重置表单时使用
    originalModel: '',
  });

  onBeforeMount(() => {
    if (props.model) {
      data.originalModel = JSON.stringify(props.model);
    }
  });

  /** 最终的规则 */
  const newRules = computed(() => data.formRules || props.rules);

  /**
   * 手动设置校验的规则 (微信小程序 props 不支持 function ，如需要自定义验证方法时需要手动设置)
   */
  function setRules(rules: $Rules) {
    data.formRules = rules;
  }

  /**
   * 重置
   */
  function resetModel() {
    if (data.originalModel) {
      const obj: typeof props.model = JSON.parse(data.originalModel);
      for (const key in props.model) {
        if (key in obj) {
          // eslint-disable-next-line vue/no-mutating-props
          props.model[key] = obj[key];
        }
      }
    }
  }

  /**
   * 清空校验结果
   */
  function clearValidate() {
    const mxpFormConfig: MxpFormConfig = formStore.state[id] || {};
    const messageInfo = mxpFormConfig.messageInfo || {};
    for (const key in messageInfo) {
      messageInfo[key] = '';
    }
  }

  /**
   * 得到需要验证的规则
   */
  function _getRules(prop: number | string, type: 'all' | 'blur' | 'change' = 'all'): $RuleItem[] {
    const rules = newRules.value[prop];
    if (rules) {
      const rulesArr = Array.isArray(rules) ? rules : [rules];
      let newRulesArr: $RuleItem[] = [];
      if (type === 'all') {
        newRulesArr = rulesArr;
      } else {
        rulesArr.forEach((rule: $RuleItem) => {
          const trigger: string[] = rule.trigger ? (Array.isArray(rule.trigger) ? rule.trigger : [rule.trigger]) : [];
          trigger.includes(type) && newRulesArr.push(rule);
        });
      }
      return newRulesArr;
    } else {
      return [];
    }
  }

  /**
   * 对部分表单字段进行校验
   */
  function validateField(prop: number | string, type: 'all' | 'blur' | 'change' = 'all'): Promise<boolean> {
    return new Promise((resolve, reject) => {
      if (formStore.state[id]?.messageInfo) {
        const rules = _getRules(prop, type);
        if (rules.length > 0) {
          nextTick(() => {
            new Schema({ [prop]: rules })
              .validate(props.model, { suppressWarning: true, suppressValidatorError: true })
              .then(() => {
                // 清空错误消息
                formStore.actions.setFormMessageInfo(id, prop, '');
                resolve(true);
              })
              .catch(({ errors, fields }) => {
                // 设置错误消息
                formStore.actions.setFormMessageInfo(id, prop, errors[0].message);
                reject({ errors, fields });
              });
          });
        } else {
          resolve(true);
        }
      } else {
        resolve(true);
      }
    });
  }

  /**
   * 验证全部字段
   */
  async function validate() {
    try {
      const arr = [];
      const mxpFormConfig: MxpFormConfig = formStore.state[id] || {};
      const messageInfo = mxpFormConfig.messageInfo || {};
      for (const key in messageInfo) {
        arr.push(validateField(key, 'all'));
      }
      await Promise.all(arr);
      return Promise.resolve(true);
    } catch ({ errors, fields }) {
      return Promise.reject(errors);
    }
  }

  defineExpose({
    /** 验证全部字段 */
    validate,
    /** 对部分表单字段进行校验 */
    validateField,
    /** 清空校验结果 */
    clearValidate,
    /** 手动设置校验的规则 (微信小程序 props 不支持 function ，如需要自定义验证方法时需要手动设置) */
    setRules,
    /** 重置 */
    resetModel,
  });
</script>

<style lang="scss" scoped>
  .mxp-form {
    display: flex;
    flex: 1;
    flex-direction: column;
    align-items: stretch;
    justify-content: center;
  }
</style>
